#include "Config.h"
#include <fstream>

Config::Config()
{
}

Config& Config::Instance()
{
	static Config _instance;
	return _instance;
}

bool Config::ParseConfigFile(std::string name)
{
	std::ifstream file;
	file.open(name.c_str());
	char line[1024];
	int i = 0, j = 0, k = 0;
	ConfigPair pair;
	char key[512], value[512];
	char inQuotes = 0;
	if (!file.good())
	{
		fprintf(stderr, "Config.ParseConfigFile: Could not open file `%s`.\n", name.c_str());
		return false;
	}
	while (file.good())
	{
		j++;
		memset(line, 0, sizeof(char) * 1024);
		file.getline(line, 1024 * sizeof(char));
		if (file.fail() && !file.eof())
		{
			fprintf(stderr, "Config.ParseConfigFile: Error. Line %d of %s is longer than 1023 characters long.\n", j, name.c_str());
			file.close();
			return false;
		}
		memset(key, 0, 512 * sizeof(char));
		memset(value, 0, 512 * sizeof(char));
		i = k = 0;
		inQuotes = 0;
		while (line[i] != 0) // read the key part of the line
		{
			if (line[i] == '\r' || line[i] == '\n' && inQuotes == 1) // found end of the line and a key portion
			{
				fprintf(stderr, "Config.ParseConfigFile: Syntax error. Expected `=` before end of line on line %d of %s.\n", j, name.c_str());
				file.close();
				return false;
			}
			if (line[i] == ' ' || line[i] == '\t')
			{
				i++;
//				inQuotes = 0;
			}
			else if (line[i] == '=')
			{
				k = 0;
				i++;
				break;
			}
			else
			{
				inQuotes = 1;
				key[k++] = line[i++];
			}
		}
		inQuotes = 0;
		while (line[i] != 0) // read the value part of the line
		{
			if (inQuotes == 0xFF && (line[i] != ' ' || line[i] != '\t' || line[i] != '\r' || line[i] != '\n'))
			{
				fprintf(stderr, "Config.ParseConfigFile: Syntax error. Unexpected character after string literal on line %d of %s.\n", j, name.c_str());
				file.close();
				return false;
			}
			if (line[i] == '\\')
			{
				if (inQuotes != 0)
				{
					value[k++] = line[i++]; // copy both characters - may add code to resolve escaped characters
					value[k++] = line[i++];
				}
				else
				{
					fprintf(stderr, "Config.ParseConfigFile: Syntax error. Unexpected '\\' at character %d on line %d of %s.\n", i, j, name.c_str());
					file.close();
					return false;
				}
			}
			else if (line[i] == '"')
			{
				if (inQuotes == '"')
				{
					inQuotes = 0xFF;
					i++;
				}
				else // normal behavior, copy character
				{
					if (inQuotes == 0)
					{
						inQuotes = '"';
						i++;
					}
					else
					{
						value[k++] = line[i++];
					}
				}
			}
			else if (line[i] == '\'')
			{
				if (inQuotes == '\'')
				{
					inQuotes = 0xFF;
					i++;
				}
				else // normal behavior, copy character
				{
					if (inQuotes == 0)
					{
						inQuotes = '\'';
						i++;
					}
					else
					{
						value[k++] = line[i++];
					}
				}
			}
			else if (inQuotes != 0)
			{
				value[k++] = line[i++];
			}
			else if (line[i] == ' ' || line[i] == '\t' || line[i] == '\r' || line[i] == '\n')
			{
				i++;
			}
			else
			{
				value[k++] = line[i++];
			}
		}
		pair.Key = key;
		pair.Value = value;
		Values[pair.Key] = pair;
		FireHook(pair.Key);
	}
	return true;
}

bool Config::HookConfigEntry(std::string key, ConfigCallback callback, void* data)
{
	HookEntry hook;
	hook.Entry = key;
	hook.Callback = callback;
	hook.CallbackData = data;
	std::vector<HookEntry> hooks;
	if (callback != NULL)
	{
		std::map<std::string, std::vector<HookEntry> >::iterator iter = Hooks.find(key);
		if (iter != Hooks.end())
		{
			iter->second.push_back(hook);
		}
		else
		{
			hooks.push_back(hook);
			Hooks[key] = hooks;
		}
		return true;
	}
	return false;
}

void Config::RemoveHook(std::string key, ConfigCallback callback)
{
	std::map<std::string, std::vector<HookEntry> >::iterator iter = Hooks.find(key);
	if (iter != Hooks.end())
	{
		std::vector<HookEntry>::iterator cb = iter->second.begin();
		while (cb != iter->second.end())
		{
			if (cb->Callback == callback)
			{
				iter->second.erase(cb);
				break;
			}
			cb++;
		}
	}
}

std::string Config::GetValue(std::string key)
{
	std::map<std::string, ConfigPair>::iterator iter = Values.find(key);
	if (iter != Values.end())
	{
		return iter->second.Value;
	}
	return "";
}
/* // Disable until I find a way to assign iterators without error
IteratePair Config::IterateValues()
{
	IteratePair pair;
	pair.Begin = Values.begin();
	pair.End = Values.end();
	return pair;
} // */

void Config::FireHook(std::string key)
{
	std::map<std::string, std::vector<HookEntry> >::iterator hook = Hooks.find(key);
	std::map<std::string, ConfigPair>::iterator pair = Values.find(key);
	if (hook != Hooks.end() && pair != Values.end())
	{
		std::vector<HookEntry>::iterator iter = hook->second.begin();
		while (iter != hook->second.end())
		{
			if (iter->Callback != NULL)
			{
				iter->Callback(pair->second.Key, pair->second.Value, iter->CallbackData);
			}
		}
	}
}